﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MongoDB.Bson.Serialization.Attributes;
using System.Reflection;

namespace nMongo
{
    internal class MongoPersistencyControlEngine
    {
        #region Singleton
        private static MongoPersistencyControlEngine _instance;

        public static MongoPersistencyControlEngine GetInstance()
        {
            lock (typeof(MongoPersistencyControlEngine))
            {
                if (_instance == null)
                    _instance = new MongoPersistencyControlEngine();
                return _instance;
            }
        } 
        #endregion

        #region Constructor
        public MongoPersistencyControlEngine()
        {
            _types = new Dictionary<KeyValuePair<Type, object>, MongoPersistencyStatistics>();
        }

        #endregion

        #region Properties
        protected IDictionary<KeyValuePair<Type, object>, MongoPersistencyStatistics> _types { get; set; } 
        #endregion

        #region Methods

        #region GetObjectPersistency (2 overloads)
        public MongoPersistencyStatistics GetObjectPersistencyByObject(Type objectType, object oneObject)
        {
            MongoPersistencyStatistics returned;
            PropertyInfo property = objectType.GetProperties()
                .FirstOrDefault(
                    x => x.CustomAttributes.Any(
                        y => y.AttributeType == typeof(BsonIdAttribute)));
            if (property != null)
            {
                returned = GetObjectPersistencyById(objectType, property.GetValue(oneObject));
            }
            else
            {
                returned = new MongoPersistencyStatistics()
                {
                    Id = null,
                    CurrentInstance = null,
                    InitialInstance = null,
                    ObjectType = objectType,
                    State = MongoPersistencyState.NotABsonClass
                };
            }
            return returned;
        }

        public MongoPersistencyStatistics GetObjectPersistencyById(Type objectType, object id)
        {
            MongoPersistencyStatistics returned;
            PropertyInfo property = objectType.GetProperties()
                .FirstOrDefault(
                    x => x.CustomAttributes.Any(
                        y => y.AttributeType == typeof(BsonIdAttribute)));
            if (property != null)
            {
                KeyValuePair<Type, object> key = new KeyValuePair<Type, object>(objectType, id);
                if (_types.Any(x => x.Key.Key == key.Key && x.Value.Id == key.Value))
                {
                    returned = _types.Single(x => x.Key.Key == key.Key && x.Value.Id == key.Value).Value;
                }
                else
                {
                    returned = new MongoPersistencyStatistics()
                    {
                        Id = key.Value,
                        CurrentInstance = null,
                        InitialInstance = null,
                        ObjectType = key.Key,
                        State = MongoPersistencyState.Unknown
                    };
                }
            }
            else
            {
                returned = new MongoPersistencyStatistics()
                {
                    Id = null,
                    CurrentInstance = null,
                    InitialInstance = null,
                    ObjectType = objectType,
                    State = MongoPersistencyState.NotABsonClass
                };
            }
            return returned;
        } 
        #endregion

        #region SetObjectPersistency (3 overloads)
        public void SetObjectPersistencyByObject(Type objectType, object oneObject, MongoPersistencyState state)
        {
            SetObjectPersistency(oneObject, state, GetObjectPersistencyByObject(objectType, oneObject: oneObject));
        }

        public void SetObjectPersistencyById(Type objectType, object id, MongoPersistencyState state)
        {
            SetObjectPersistency(null, state, GetObjectPersistencyById(objectType, id: id));
        }

        protected void SetObjectPersistency(object oneObject, MongoPersistencyState state, MongoPersistencyStatistics control)
        {
            Type objectType = control.ObjectType;
            object id = control.Id;
            if (control.State == MongoPersistencyState.Unknown)
            {
                control = new MongoPersistencyStatistics()
                {
                    Id = id,
                    CurrentInstance = oneObject,
                    InitialInstance = oneObject,
                    ObjectType = objectType,
                    State = state
                };
                _types.Add(control.GetListKey(), control);
            }
            else
            {
                control.CurrentInstance = oneObject;
                control.State = state;
                _types[control.GetListKey()] = control;
            }
        } 
        #endregion

        #endregion
    }
}
